package utils.dataaccess;

import entities.*;
import java.util.ArrayList;
import java.util.Date;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.SessionFactory;



public class HibernateUtil implements IDatabaseUtil{
    private  final SessionFactory sessionFactory;

     {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public  SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public  Session getSession() {
        return sessionFactory.getCurrentSession();
    }
    
    
    @Override
    public  ArrayList<Systemobserver> getStatistics(){
        Session session = getSession();
        session.getTransaction().begin();
         Query eventsQuery = session.createQuery("select E from Systemobserver E");
         eventsQuery.setFetchSize(5);
         ArrayList<Systemobserver> statistics = (ArrayList<Systemobserver>)eventsQuery.list();
        session.getTransaction().commit();
        return statistics;
    }

    @Override
     public  void changeGroup(Authorisation user, int groupID) {
        Session session = getSession();
        session.getTransaction().begin();
        user.setGid(groupID);
        session.update(user);
        session.getTransaction().commit();
    }

    @Override
    public  void delete(Object entity) {
        Session session = getSession();
        session.getTransaction().begin();
        session.delete(entity);
        session.getTransaction().commit();
        updateEventCounter();
    }

    @Override
    public  Forumblocks getBlockById(int blockID) {
        Session session = getSession();
        session.getTransaction().begin();
        Query blockQuery = session.createQuery("select B from Forumblocks B where B.id=:bid");
        blockQuery.setInteger("bid", blockID);
        Forumblocks block = (Forumblocks) blockQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return block;
    }

    @Override
    public  Forumblocks getBlockByName(String value) {
        Session session = getSession();
        session.getTransaction().begin();
        Query blockQuery = session.createQuery("select B from Forumblocks B where B.name=:name");
        blockQuery.setString("name", value);
        Forumblocks block = (Forumblocks) blockQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return block;
    }

    @Override
    public  ArrayList<Forumblocks> getBlocks() {
        Session session = getSession();
        session.getTransaction().begin();
        Query blocksQuery = session.createQuery("from Forumblocks");
        ArrayList<Forumblocks> blocks = (ArrayList<Forumblocks>) blocksQuery.list();
        session.getTransaction().commit();
        updateEventCounter();
        return blocks;
    }

    @Override
    public  ArrayList<Forumthreads> getBlocksThreads(Forumblocks block) {
        Session session = getSession();
        session.getTransaction().begin();
        Query treadsQuery = session.createQuery("from Forumthreads where bid=:bid");
        treadsQuery.setInteger("bid", (int) block.getId());
        ArrayList<Forumthreads> threads = (ArrayList<Forumthreads>) treadsQuery.list();
        session.getTransaction().commit();
        updateEventCounter();
        return threads;
    }

    @Override
    public  Authorisation getByEmail(String email) {
        Session session = getSession();
        session.getTransaction().begin();
        Query authQuery = session.createQuery("select entry from Authorisation entry where entry.email=:email");
        authQuery.setString("email", email);
        Authorisation authEntry = (Authorisation) authQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return authEntry;
    }

    //ADMINISTRATIVE METHODS
    @Override
    public  ArrayList<Authorisation> getEntries() {
        Session session = getSession();
        session.getTransaction().begin();
        Query authQuery = session.createQuery("select A from Authorisation A order by A.username");
        ArrayList<Authorisation> entries = (ArrayList<Authorisation>) authQuery.list();
        session.getTransaction().commit();
        return entries;
    }

    @Override
    public  Authorisation getEntry(String username) {
        Session session = getSession();
        session.getTransaction().begin();
        Query authQuery = session.createQuery("select entry " + "from Authorisation entry where " + "entry.username=:username");
        authQuery.setString("username", username);
        Authorisation authEntry = (Authorisation) authQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return authEntry;
    }

    @Override
    public  Authorisation getEntry(String username, String password) {
        Session session = getSession();
        session.getTransaction().begin();
        Query authQuery = session.createQuery("select entry " + "from Authorisation entry where " + "entry.username=:username " + "and entry.password=:password");
        authQuery.setString("username", username);
        authQuery.setString("password", password);
        Authorisation authEntry = (Authorisation) authQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return authEntry;
    }

    @Override
    public  ArrayList<Forumposts> getPosts(Forumthreads thread) {
        Session session = getSession();
        session.getTransaction().begin();
        Query postsQuery = session.createQuery("select T from Forumposts T where T.tid=:tid order by T.datetime desc");
        postsQuery.setInteger("tid", (int) thread.getId());
        ArrayList<Forumposts> posts = (ArrayList<Forumposts>) postsQuery.list();
        session.getTransaction().commit();
        updateEventCounter();
        return posts;
    }

    @Override
    public  ArrayList<Forumposts> getPosts(String pattern) {
        Session session = getSession();
        session.getTransaction().begin();
        Query postsQuery = session.createQuery("select P from Forumposts P where P.text like '%" + pattern + "%' order by P.datetime desc");
        ArrayList<Forumposts> posts = (ArrayList<Forumposts>) postsQuery.list();
        session.getTransaction().commit();
        updateEventCounter();
        return posts;
    }

    @Override
    public  ArrayList<Forumposts> getPosts(Authorisation user) {
        Session session = getSession();
        session.getTransaction().begin();
        Query postsQuery = session.createQuery("select P from Forumposts P where P.author=:author");
        postsQuery.setString("author", user.getUsername());
        ArrayList<Forumposts> posts = (ArrayList<Forumposts>) postsQuery.list();
        session.getTransaction().commit();
        return posts;
    }

    @Override
    public  Systemobserver getStatistics(Date date) {
        Session session = getSession();
        session.getTransaction().begin();
        Query eventsQuery = session.createQuery("select E from Systemobserver E where E.datestamp=:datestamp");
        eventsQuery.setParameter("datestamp", date);
        Systemobserver statistics = (Systemobserver) eventsQuery.uniqueResult();
        session.getTransaction().commit();
        return statistics;
    }

    @Override
    public  Forumthreads getThreadById(int threadID) {
        Session session = getSession();
        session.getTransaction().begin();
        Query threadQuery = session.createQuery("select T from Forumthreads T where T.id=:tid");
        threadQuery.setInteger("tid", threadID);
        Forumthreads thread = (Forumthreads) threadQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return thread;
    }

    @Override
    public  void increaseUserCounter() {
        Systemobserver oneDayStatistics = getStatistics(new Date());
        Session session = getSession();
        session.getTransaction().begin();
        if (oneDayStatistics == null) {
            oneDayStatistics = new Systemobserver(new Date(), 1, 1);
            session.save(oneDayStatistics);
        } else {
            oneDayStatistics.setUsers(oneDayStatistics.getUsers() + 1);
            session.merge(oneDayStatistics);
        }
        session.getTransaction().commit();
    }

    @Override
    public  boolean isRegistered(String username) {
        Session session = getSession();
        session.getTransaction().begin();
        Query authQuery = session.createQuery("select entry " + "from Authorisation entry where " + "entry.username=:username");
        authQuery.setString("username", username);
        Authorisation authEntry = (Authorisation) authQuery.uniqueResult();
        session.getTransaction().commit();
        updateEventCounter();
        return authEntry == null ? false : true;
    }

    @Override
    public  void moveThread(Forumthreads thread, Forumblocks sourceBlock, Forumblocks targetBlock) {
        Session session = getSession();
        session.getTransaction().begin();
        thread.setBid((int) targetBlock.getId());
        targetBlock.setThreadnumber(targetBlock.getThreadnumber() + 1);
        targetBlock.setPostsnumber(targetBlock.getPostsnumber() + thread.getPostsnumber());
        sourceBlock.setThreadnumber(sourceBlock.getThreadnumber() - 1);
        sourceBlock.setPostsnumber(sourceBlock.getPostsnumber() - thread.getPostsnumber());
        session.update(thread);
        session.update(sourceBlock);
        session.update(targetBlock);
        session.getTransaction().commit();
    }

    @Override
    public  void persist(Object entity) {
        Session session = getSession();
        session.getTransaction().begin();
        session.merge(entity);
        session.getTransaction().commit();
        updateEventCounter();
    }

    @Override
    public  void save(Object entity) {
        Session session = getSession();
        session.getTransaction().begin();
        session.save(entity);
        session.getTransaction().commit();
        updateEventCounter();
    }

    @Override
    public  void updateEventCounter() {
        Systemobserver oneDayStatistics = getStatistics(new Date());
        Session session = getSession();
        session.getTransaction().begin();
        if (oneDayStatistics == null) {
            oneDayStatistics = new Systemobserver(new Date(), 0, 0);
            session.save(oneDayStatistics);
        } else {
            oneDayStatistics.setEvents(oneDayStatistics.getEvents() + 1);
            session.merge(oneDayStatistics);
        }
        session.getTransaction().commit();
    }


}
